import base64

from flask import Flask, Blueprint, jsonify, request
import cv2
import torch
import time
import numpy as np
from datetime import datetime
import json


class QrcodeQualityDetector:
    DETECTIONS_NUMBERS = 10  # 选择检测次数
    SELECT_CAMERA = 0  # 选择摄像头
    SELECT_QR_LOCATION = 0  # 选择二维码位置
    SAVE_IMAGE_FILE = 'frame.bmp'  # 保存图片文件
    OBJECT_DETECTION_MODEL1 = 'detect.prototxt'  # 物体检测的模型1
    OBJECT_DETECTION_MODEL2 = 'detect.caffemodel'  # 物体检测的模型2
    IMAGE_RESOLUTION_MODEL1 = 'sr.prototxt'  # 图像超分辨率的模型1
    IMAGE_RESOLUTION_MODEL2 = 'sr.caffemodel'  # 图像超分辨率的模型2

    # 定义变量
    def __init__(self):
        self.array_data_list = []  # 保存二维码坐标
        self.res = "没有获取到二维码信息"  # 用于保存二维码信息
        self.state = ""  # 用于保存质检状态
        self.is_pass = False  # 用于保存质检是否成功
        self.qrcode_is_black = True
        self.enable_write_image = False
        self.box_points = []  # 保存物块坐标
        self.have_box = False
        self.offset = []  # 用于保存左上角坐标的列表

    def initialization(self):
        self.array_data_list = []  # 保存二维码坐标
        self.res = "没有获取到二维码信息"  # 用于保存二维码信息
        self.state = ""  # 用于保存质检状态
        self.is_pass = False  # 用于保存质检是否成功
        self.box_points = []  # 保存物块坐标
        self.have_box = False
        self.offset = []  # 用于保存左上角坐标的列表

    def set_qrcode_is_black(self, qrcode_is_black):
        self.qrcode_is_black = qrcode_is_black

    # 返回质检信息
    def get_state(self):
        return self.state

    # 获取图片并根据QR码颜色修改图片
    def __get_image(self):
        self.state = "正在读取图像"
        cap = cv2.VideoCapture(self.SELECT_CAMERA)  # 选择使用的摄像头
        if cap is not None:
            # 读取一帧图像
            ret, frame = cap.read()
            if ret:
                self.frame = frame
                return frame

        else:
            # 无法打开摄像头或读取图像
            print("无法打开摄像头或读取图像")
        # 关闭摄像头资源
        cap.release()

    # QR码模型的识别
    def __detect_and_decode(self, frame):
        detect_obj = cv2.wechat_qrcode_WeChatQRCode(self.OBJECT_DETECTION_MODEL1, self.OBJECT_DETECTION_MODEL2,
                                                    self.IMAGE_RESOLUTION_MODEL1,
                                                    self.IMAGE_RESOLUTION_MODEL2)  # 使用的二维码识别模型
        self.state = "正在确定二维码位置"
        # 使用模型获取图片中二维码的内容和位置
        res, points = detect_obj.detectAndDecode(frame)

        # 如果检测到二维码内容
        if res:
            self.res = res
            self.is_pass = True
            # 循环遍历所有坐标，并将偏移量应用到它们上
            array_data_list = points[self.SELECT_QR_LOCATION].tolist()
            if not self.offset:
                self.array_data_list = array_data_list
            else:
                for i in range(len(array_data_list)):
                    array_data_list[i][0] += self.offset[0][0]  # 将偏移量添加到 x 坐标
                    array_data_list[i][1] += self.offset[0][1]  # 将偏移量添加到 y 坐标
                self.array_data_list = array_data_list

    def crop_image_box(self, image):
        result = image  # 输出的图像
        model = torch.hub.load('weights', 'custom', 'weights/best_box.pt', source='local')
        results = model(image)
        if results:
            self.have_box = True
            bboxes = results.xyxy[0]  # 边界框的信息
            class_labels = results.names[0]  # 目标名字
            confidences = bboxes[:, 4]  # 所有边界框的置信度
            filtered_bboxes = bboxes[confidences > 0.5]  # 筛选出置信度大于0.5的边界框
            # 裁剪图片获取目标
            for bbox in filtered_bboxes:
                xmin, ymin, xmax, ymax, _, class_idx = bbox
                self.offset.append((int(xmin), int(ymin)))  # 记录左上角坐标
                result = image[int(ymin):int(ymax), int(xmin):int(xmax)]
            return result

    def get_boxes_points(self, image):
        result = self.crop_image_box(image)  # 输出的图像
        # 定义颜色区间
        lower_color = np.array([140, 180, 190])
        upper_color = np.array([170, 215, 220])
        # 创建掩码
        mask = cv2.inRange(result, lower_color, upper_color)
        # 形态学转换：闭操作
        kernel = np.ones((5, 5), np.uint8)
        closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
        # 找到白色轮廓
        contours, _ = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        # 如果有轮廓被找到
        if contours:
            # 获取最大轮廓
            max_contour = max(contours, key=cv2.contourArea)
            # 找到最上、最下、最左和最右的点
            top_point = tuple(max_contour[max_contour[:, :, 1].argmin()][0])
            top_point_ok = (top_point[0] + self.offset[0][0], top_point[1] + self.offset[0][1])
            bottom_point = tuple(max_contour[max_contour[:, :, 1].argmax()][0])
            bottom_point_ok = (bottom_point[0] + self.offset[0][0], bottom_point[1] + self.offset[0][1])
            left_point = tuple(max_contour[max_contour[:, :, 0].argmin()][0])
            left_point_ok = (left_point[0] + self.offset[0][0], left_point[1] + self.offset[0][1])
            right_point = tuple(max_contour[max_contour[:, :, 0].argmax()][0])
            right_point_ok = (right_point[0] + self.offset[0][0], right_point[1] + self.offset[0][1])
            # 将四个点的坐标信息存入列表
            self.box_points.append(
                {'top': top_point_ok, 'bottom': bottom_point_ok, 'left': left_point_ok, 'right': right_point_ok})

        return result

    def doing1(self, image):
        self.state = "准备开始质检"  # 初始化质检状态
        if image is not None:
            # 初始化帧
            frame = None
            if not self.qrcode_is_black:
                # 反转图像颜色
                frame = cv2.bitwise_not(image)
            else:
                frame = image  # 若无需反转颜色，则直接使用原始图像

            # 获取边框点
            frame_with_boxes = self.get_boxes_points(frame)

            # 如果存在边框
            if self.have_box:
                # 进行检测和解码
                self.__detect_and_decode(frame_with_boxes)
                self.state = "正在分析质检信息"
                if self.is_pass:
                    self.state = "正在返回质检信息"

        # 返回质检信息和相关状态
        return self.array_data_list, self.res, self.is_pass, self.box_points

    def doing(self):
        self.state = "准备开始质检"  # 初始化质检状态
        for _ in range(self.DETECTIONS_NUMBERS):
            frame = self.__get_image()
            if frame is not None:
                # 成功读取到图像
                if self.enable_write_image:
                    cv2.imwrite(self.SAVE_IMAGE_FILE, frame)

                if not self.qrcode_is_black:
                    # 反转图像颜色
                    frame = cv2.bitwise_not(frame)

                self.__detect_and_decode(frame)
                self.state = "正在分析质检信息"
                if self.is_pass:
                    self.state = "正在返回质检信息"
                    break
                else:
                    continue
            else:
                continue
        return self.array_data_list, self.res, self.is_pass


class QrcodeQualityDetectorServer:
    BLUEPRINT_NAME = 'qrcode_quality'

    def __init__(self):
        self.quality_detector = QrcodeQualityDetector()
        self.qrcode_quality_bp = Blueprint(self.BLUEPRINT_NAME, __name__)
        self.qrcode_quality_bp.route('/process_image', methods=['GET'])(self.process_image)
        self.qrcode_quality_bp.route('/process_image_data', methods=['GET'])(self.process_image_data)
        self.qrcode_quality_bp.route('/state', methods=['GET'])(self.get_state)
        self.qrcode_quality_bp.route('/set_qrcode_color', methods=['GET'])(self.set_qrcode_color)
        self.qrcode_quality_bp.route('/verify_link', methods=['GET'])(self.verify_link)
        self.qrcode_quality_bp.route('/process_image_verifyApp', methods=['POST'])(self.process_image_verifyApp)
        self.app = Flask(self.BLUEPRINT_NAME)
        if self.app is not None:
            self.app.register_blueprint(self.qrcode_quality_bp)
            self.app.run(host='0.0.0.0', port=5000)

    def process_image_verifyApp(self):
        try:
            self.quality_detector.initialization()
            data = request.json
            encoded_image = data.get('image')
            height = data.get('height')
            width = data.get('width')
            channels = data.get('channels')
            image_data = base64.b64decode(encoded_image)
            # 将字节数据转换为 NumPy 数组
            image_array = np.frombuffer(image_data, dtype=np.uint8)
            image = image_array.reshape((height, width, channels))
            qr_points, res, is_pass, box_points = self.quality_detector.doing1(image)
            # 转换元组中的 int32 类型为 int 类型
            box_points = [{key: tuple(int(coord) for coord in value) for key, value in point.items()} for point in
                          box_points]
            angle_offset = '100'
            if is_pass:
                angle_offset = '0'
            # 获取当前时间戳
            current_time_stamp = time.time()
            # 将时间戳转换为datetime对象
            current_datetime = datetime.fromtimestamp(current_time_stamp)

            # 将datetime对象格式化为年月日时分秒
            formatted_date = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
            response_data = {
                'qr_angle_offset': angle_offset,
                'qr_points': qr_points,
                'qr_info': res,
                'qr_is_pass': is_pass,
                'time': formatted_date,
                'box_points': box_points
            }

            return jsonify(response_data)

        except Exception as e:
            print(str(e))
            return jsonify({'error': str(e)})

    def verify_link(self):
        try:
            verify_link = True
            response_data = {'verify_link': verify_link}
            return jsonify(response_data)

        except Exception as e:
            return jsonify({'error': str(e)})

    def set_qrcode_color(self):
        try:
            qrcode_is_black = request.args.get('qrcode_is_black')
            qrcode_is_black = qrcode_is_black.lower() == 'true'
            self.quality_detector.set_qrcode_is_black(qrcode_is_black)

            return jsonify({'success': True})  # Returning a valid response
        except Exception as e:
            return jsonify({'error': str(e)})

    def process_image(self):
        try:
            self.quality_detector.initialization()
            array_data_list, res, is_pass = self.quality_detector.doing()
            response_data = {'is_pass': is_pass}
            return jsonify(response_data)

        except Exception as e:
            return jsonify({'error': str(e)})

    def process_image_data(self):
        try:
            self.quality_detector.initialization()
            array_data_list, res, is_pass = self.quality_detector.doing()
            angle_offset = '0'
            is_position_correct = False
            if is_pass:
                is_position_correct = True
            response_data = {
                'angle_offset': angle_offset,
                'info': res,
                'is_pass': is_pass,
                'is_position_correct': is_position_correct
            }

            return jsonify(response_data)

        except Exception as e:
            return jsonify({'error': str(e)})

    def get_state(self):
        self.quality_detector.initialization()
        return self.quality_detector.get_state()


if __name__ == '__main__':
    quality_detector_server = QrcodeQualityDetectorServer()
